package io.summer.gadget.enums;

import io.summer.basis.tool.MyReflectionTool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Moonlight
 */
public class EnumScanner {
  private static final Logger log = LoggerFactory.getLogger(EnumScanner.class);

  private final Map<String, List<EnumDto<?>>> enums;

  public static EnumScanner createEnumScanner(Collection<String> packages) {
    return new EnumScanner(packages);
  }

  private EnumScanner(Collection<String> packages) {
    enums = new HashMap<>();
    try {
      doScan(packages);
    } catch (IllegalAccessException e) {
      log.error("EnumScanner: ", e);
    }
  }

  public Map<String, List<EnumDto<?>>> get(String... names) {
    if (names == null || names.length == 0) {
      return enums;
    }
    Map<String, List<EnumDto<?>>> sub = new HashMap<>(names.length);
    for (String name : names) {
      if (enums.containsKey(name)) {
        sub.put(name, enums.get(name));
      }
    }
    return sub;
  }

  private String getEnumName(Class<?> clazz) {
    ApiEnum annotation = clazz.getAnnotation(ApiEnum.class);
    return StringUtils.hasText(annotation.value()) ? clazz.getSimpleName() : annotation.value();
  }

  private EnumDto<?> buildEnumDto(Enum<?> e, Class<?> clazz) throws IllegalAccessException {
    EnumDto<Enum<?>> enumDto = new EnumDto<>();
    enumDto.setValue(e);
    enumDto.setOrdinal(e.ordinal());
    Field[] fields = clazz.getDeclaredFields();
    Map<String, Object> values = new HashMap<>(fields.length);
    for (Field field : fields) {
      if (Modifier.isStatic(field.getModifiers())) {
        continue;
      }
      //cancel accessible check.
      field.setAccessible(true);
      values.put(field.getName(), field.get(e));
    }
    enumDto.setFields(values);

    return enumDto;
  }

  private void doScan(Collection<String> packages) throws IllegalAccessException {
    if (CollectionUtils.isEmpty(packages)) {
      return;
    }
    for (String pack : packages) {
      List<Class<?>> classes = MyReflectionTool.getClassWithAnnotations(pack, ApiEnum.class);
      for (Class<?> clazz : classes) {
        if (!clazz.isEnum()) {
          log.warn("!do NOT put @ApiEnum on none enum type: {}", clazz.getName());
          continue;
        }
        List<EnumDto<?>> values = new ArrayList<>();
        Enum<?>[] ee = (Enum<?>[]) clazz.getEnumConstants();
        for (Enum<?> e : ee) {
          values.add(buildEnumDto(e, clazz));
        }
        values.sort(Comparator.comparingInt(EnumDto::getOrdinal));
        enums.put(getEnumName(clazz), values);
      }
    }
  }
}
